package mobilesmil.services;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import mobilesmil.activity.SendActivity;
import mobilesmil.data.SmilDataSet;
import mobilesmil.data.SmilResource;
import mobilesmil.xml.parsing.SmilParser;

import org.apache.commons.net.ftp.FTPClient;

import android.os.Environment;
import android.util.Log;
/**
 * This is the sending thread that compresses all the files for a smil message, updates the database and pushes the message onto the server.
 */
public class MessageSender implements Runnable
{
	private SendActivity mContext;
	private String mToPhoneNumber;
	private String mFromPhoneNumber;
	private String mFileName;
	private String mMessageUrl;
	private static final int BUFFER = 16384;
	
	/**
	 * This instantiates the class variables so that neccessary values are there for a message to be sent.
	 * @param toPhoneNumber
	 * @param fromPhoneNumber
	 * @param fileName
	 * @param act
	 */
	public MessageSender(String toPhoneNumber, String fromPhoneNumber, String fileName, SendActivity act)
	{
		mToPhoneNumber = toPhoneNumber;
		mFromPhoneNumber = fromPhoneNumber;
		mFileName = fileName;
		mContext = act;
	}

	@Override
	public void run() 
	{
		String zipFile;
		zipFile = compressSmilResources();	
		//was there successful file compression
		if(zipFile != "")
		{
			//yes, was the file pushed to the server correctly?
			if(sendZipFile(zipFile))
			{
				//yes, was there successful update of the database
				if(updateDatabase(zipFile))
					//yes, display message sent
					mContext.showMessage("Message sent");
				else
					//no
					mContext.showMessage("Message Failed");
			}
			else
			{
				//the file was not sent to the server
				mContext.showMessage("Message Failed");
			}
			//clean up the zip file on the local device
			deleteZipFile(zipFile);
		}
		else
		{
			//the message was not successfully compressed
			mContext.showMessage("Message Failed");
		}
		
	}
	
	/**
	 * Get the array of files to be zipped and then return the result of the compression method
	 * @return
	 */
	private String compressSmilResources()
	{
		SmilDataSet smilResources = new SmilDataSet();
		try
		{
			smilResources = SmilParser.getParsedDataSet(mFileName);
		}
		catch(Exception exc)
		{
			Log.e("Resouces could not be generated", exc.toString(), exc);
		}
		
		ArrayList<SmilResource> smilResourceList;
		ArrayList<String> files = new ArrayList<String>();
		smilResourceList = smilResources.getResourcesByBeginTime();
		
		int numResources = smilResourceList.size();
		
		for(int index = 0; index < numResources;index++)
		{
			boolean addResource = true;
			String smilResourceSourceName = smilResourceList.get(index).getSource();
			
			//filter keep resources from being added twice to the zip file
			for(int fileIndex = 0; fileIndex < files.size(); fileIndex++)
			{
				if(smilResourceSourceName.equals(files.get(fileIndex)))
				{
					addResource = false;
					break;
				}
			}
			if(addResource)
				files.add(smilResourceSourceName);
		}
		
		return zipFiles(files);
	}
	
	
	/**
	 * this function takes an array list of the files to be zipped and compresses them.  It returns the name of the compressed files
	 * @param files
	 * @return
	 */
	public String zipFiles(ArrayList<String> files)
	{
		String filePath = "";
		String fileWithExtentsion;
		String zipFile = "";
		//fix the filename
		if (mFileName.endsWith(".smil") == false)
		{
			fileWithExtentsion = mFileName + ".smil";
		}
		else
			fileWithExtentsion = mFileName;
		
		//access the sd card
		filePath = Environment.getExternalStorageDirectory().toString() + "/" + mFileName;
		
		//add the smil to the list of files to be zipped
		files.add(fileWithExtentsion);
		
		try
		{
			BufferedInputStream origin = null;
			FileOutputStream dest = new FileOutputStream(filePath.replace(".smil", "") + ".zip");
			zipFile = mFileName.replace(".smil", "") + ".zip";
			ZipOutputStream out = new ZipOutputStream(dest);
			byte data[] = new byte[BUFFER];
			
			//zip the files
			for(int index = 0; index < files.size(); index++)
			{
				FileInputStream fi = new FileInputStream(Environment.getExternalStorageDirectory().toString() + "/" + files.get(index));
				origin = new BufferedInputStream(fi, BUFFER);
				ZipEntry entry = new ZipEntry(files.get(index));
				out.putNextEntry(entry);
				int count;
				while((count=origin.read(data,0,BUFFER)) != -1)
				{
					out.write(data,0,count);
					
				}
						
				origin.close();
			}
			out.close();
			
		}
		catch(Exception exc)
		{
			Log.e("Failed Compressing Message", exc.toString(), exc);
		}
		return zipFile;
	}
	
	/**
	 * Takes the zip file and adds and pushes it to the server
	 * @param zipFile
	 * @return
	 */
	private boolean sendZipFile(String zipFile)
	{
		//update the zip file to a date and time to make it unique
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmssSS");
		String timestamp = dateFormat.format(Calendar.getInstance().getTime());
		mMessageUrl = zipFile.replaceAll(".zip", "") + "_" + timestamp + ".zip";
		
		try {
			//upload the zip file to the actual ftp server
			FTPClient ftpClient = null;
			BufferedInputStream buffIn = null;
			try {
				ftpClient = new FTPClient();
				ftpClient.connect(InetAddress.getByName(SmilMessages.SERVER_HOST));
				ftpClient.login(SmilMessages.SERVER_USERNAME, SmilMessages.SERVER_PASSWORD);
				ftpClient.changeWorkingDirectory(SmilMessages.SERVER_SMIL_DIR);
				ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
				ftpClient.setBufferSize(BUFFER);
				buffIn = null;
				buffIn=new BufferedInputStream(new FileInputStream(new File(Environment.getExternalStorageDirectory() + "/" + zipFile)));
				ftpClient.enterLocalPassiveMode();
				ftpClient.storeFile(mMessageUrl, buffIn);
			} catch (Exception e) { throw e; }
			finally {
				if(buffIn != null)
					buffIn.close();
				if(ftpClient != null) {
					ftpClient.logout();
					ftpClient.disconnect();
				}
			}
			return true;
		} catch(Exception e) {
			Log.e("whoops", e.toString(), e);
			return false;
		}
	}
	
	/**
	 * makes a call to the send php script which will update the database with the new message
	 * @param zipFile
	 * @return
	 */
	private boolean updateDatabase(String zipFile)
	{
		String url = SmilMessages.SERVER_PHP_ABSOLUTE_DIR + "send.php?";
		url += "to_number=" 	+ "\"" + mToPhoneNumber 	+ "\"";
		url += "&from_number=" 	+ "\"" + mFromPhoneNumber 	+ "\"";
		url += "&message_url=" 	+ "\"" + mMessageUrl 		+ "\"";
		
		try
		{
			URL sendURL = new URL(url);
			URLConnection sendScript = sendURL.openConnection();
			BufferedReader in = new BufferedReader(new InputStreamReader(sendScript.getInputStream()));
			String responseLine = "";
			
			StringBuffer fullResponse = new StringBuffer();
			while((responseLine = in.readLine())!=null)
			{
				fullResponse.append(responseLine);
			}
			if(fullResponse.toString().contains("yes"))
				return true;
			else
				return false;
		}
		catch(Exception exc)
		{
			Log.e("Failed updated database", exc.toString(), exc);
			return false;
		}
	}
	
	/**
	 * This deletes a file from the sd card.
	 * @param zipFile
	 */
	private void deleteZipFile(String zipFile)
	{
		File message = new File(Environment.getExternalStorageDirectory() + "/" + zipFile);
		if(message.exists())
			message.delete();
	}
	
}
